package net.simflow.swf.analyzer.exec.stats.opt;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import net.simflow.swf.analyzer.exec.data.TaskExecResult;
import net.simflow.swf.analyzer.exec.stats.data.FlowExecTime;
import net.simflow.swf.analyzer.network.data.Flow;

public class FlowExecTimeAnalyzer {

	private static FlowExecTimeAnalyzer instance = new FlowExecTimeAnalyzer();

	private FlowExecTimeAnalyzer() {
	}

	public static FlowExecTimeAnalyzer getInstance() {
		return instance;
	}

	public HashMap<Flow, FlowExecTime> analyzeExecTime(
			Collection<? extends TaskExecResult> taskExecSet) {
		HashMap<Flow, FlowExecTime> map = new HashMap<Flow, FlowExecTime>();

		if (taskExecSet == null) {
			return map;
		}

		HashMap<Flow, Integer> maxMap = new HashMap<Flow, Integer>();
		HashMap<Flow, Integer> minMap = new HashMap<Flow, Integer>();
		HashMap<Flow, Double> sumMap = new HashMap<Flow, Double>();
		HashMap<Flow, Integer> countMap = new HashMap<Flow, Integer>();

		for (TaskExecResult res : taskExecSet) {
			Flow flow = res.getFlow();
			int execTime = res.getExecTime();
			if (execTime == -1) {
				continue;
			}

			Integer max = maxMap.get(flow);
			if (max == null || execTime > max) {
				maxMap.put(flow, execTime);
			}

			Integer min = minMap.get(flow);
			if (min == null || execTime < min) {
				minMap.put(flow, execTime);
			}

			Double sum = sumMap.get(flow);
			if (sum == null) {
				sumMap.put(flow, (double) execTime);
			} else {
				sumMap.put(flow, sum + execTime);
			}

			Integer count = countMap.get(flow);
			if (count == null) {
				count = 0;
			}
			count++;
			countMap.put(flow, count);
		}

		for (Flow flow : sumMap.keySet()) {
			double sum = sumMap.get(flow);
			int count = countMap.get(flow);
			double avg = sum / count;
			int max = maxMap.get(flow);
			int min = minMap.get(flow);

			map.put(flow, new FlowExecTime(flow, max, min, avg));
		}

		return map;
	}

	public TreeMap<Integer, ArrayList<Flow>> analyzeMaxExecTimeDistribution(
			Map<Flow, FlowExecTime> execTimeMap) {
		TreeMap<Integer, ArrayList<Flow>> map = new TreeMap<Integer, ArrayList<Flow>>();

		if (execTimeMap == null) {
			return map;
		}

		for (Entry<Flow, FlowExecTime> en : execTimeMap.entrySet()) {
			Flow flow = en.getKey();
			int time = en.getValue().getMaxTime();
			ArrayList<Flow> a = map.get(time);
			if (a == null) {
				a = new ArrayList<Flow>();
				map.put(time, a);
			}
			a.add(flow);
		}
		map.comparator();

		return map;
	}

	public TreeMap<Integer, ArrayList<Flow>> analyzeMinExecTimeDistribution(
			Map<Flow, FlowExecTime> execTimeMap) {
		TreeMap<Integer, ArrayList<Flow>> map = new TreeMap<Integer, ArrayList<Flow>>();

		if (execTimeMap == null) {
			return map;
		}

		for (Entry<Flow, FlowExecTime> en : execTimeMap.entrySet()) {
			Flow flow = en.getKey();
			int time = en.getValue().getMinTime();
			ArrayList<Flow> a = map.get(time);
			if (a == null) {
				a = new ArrayList<Flow>();
				map.put(time, a);
			}
			a.add(flow);
		}
		map.comparator();

		return map;
	}

	public TreeMap<Double, ArrayList<Flow>> analyzeAvgExecTimeDistribution(
			Map<Flow, FlowExecTime> execTimeMap) {
		TreeMap<Double, ArrayList<Flow>> map = new TreeMap<Double, ArrayList<Flow>>();

		if (execTimeMap == null) {
			return map;
		}

		for (Entry<Flow, FlowExecTime> en : execTimeMap.entrySet()) {
			Flow flow = en.getKey();
			double time = en.getValue().getAvgTime();
			ArrayList<Flow> a = map.get(time);
			if (a == null) {
				a = new ArrayList<Flow>();
				map.put(time, a);
			}
			a.add(flow);
		}
		map.comparator();

		return map;
	}
}
